﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;

namespace Dycox
{
    public static class ReflectionHelpers
    {
        public static object GetValue(this MemberInfo member, object instance)
        {
            if (member == null)
                throw new ArgumentNullException("member");

            PropertyInfo p = member as PropertyInfo;

            if (p != null)
            {
                return p.GetValue(instance, null);
            }

            FieldInfo f = member as FieldInfo;

            if (f != null)
            {
                return f.GetValue(instance);
            }

            MethodInfo m = member as MethodInfo;

            if (m != null)
            {
                if (m.GetParameters().Length > 0)
                    throw new ArgumentException(SR.GetString("method_cannot_has_parameter"));
                
                return m.Invoke(instance, null);
            }

            throw new NotSupportedException(SR.GetString("member_type_not_supported", member.MemberType));
        }

        public static void SetValue(this MemberInfo member, object instance, object value)
        {
            if (member == null)
                throw new ArgumentNullException("member");

            PropertyInfo p = member as PropertyInfo;

            if (p != null)
            {
                p.SetValue(instance, value, null);
                return;
            }

            FieldInfo f = member as FieldInfo;

            if (f != null)
            {
                f.SetValue(instance, value);
                return;
            }

            MethodInfo m = member as MethodInfo;

            if (m != null)
            {
                if (m.GetParameters().Length != 1)
                    throw new ArgumentException(SR.GetString("method_must_have_one_parameter"));

                m.Invoke(instance, new object[] { value });
            }

            throw new NotSupportedException(SR.GetString("member_type_not_supported", member.MemberType));
        }

        public static MemberInfo GetPropertyOrField(this Type type, string name)
        {
            return GetPropertyOrField(type, name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
        }

        public static MemberInfo GetPropertyOrField(this Type type, string name, BindingFlags flags)
        {
            var array = type.GetMember(name, MemberTypes.Property | MemberTypes.Field, flags);

            if (array.Length > 0) return array[0];
            else return null;
        }

        public static T GetCustomAttribute<T>(this PropertyDescriptor property)
            where T : Attribute
        {
            if (property == null)
                throw new ArgumentNullException("property");

            return (T)property.Attributes[typeof(T)];
        }

        public static T GetCustomAttribute<T>(this MemberInfo member)
            where T : Attribute
        {
            if (member == null)
                throw new ArgumentNullException("member");

            return (T)Attribute.GetCustomAttribute(member, typeof(T));
        }

        public static T GetCustomAttribute<T>(this Type type)
            where T : Attribute
        {
            if (type == null)
                throw new ArgumentNullException("type");

            return (T)Attribute.GetCustomAttribute(type, typeof(T));
        }

        public static TypeFlags GetTypeFlags(this Type type)
        {
            TypeFlags flags = TypeFlags.None;

            if (type.IsGenericType) flags |= TypeFlags.Generic;

            if (type.IsClass)
            {
                flags |= TypeFlags.Class;
                if (type.IsArray) flags |= TypeFlags.Array;
            }
            else if (type.IsValueType)
            {
                if (type.IsGenericType)
                {
                    if (type.GetGenericTypeDefinition() == typeof(Nullable<>))
                    {
                        type = Nullable.GetUnderlyingType(type);
                        System.Diagnostics.Debug.Assert(type != null);
                        flags |= TypeFlags.Nullable;
                    }
                }


                if (!type.IsEnum)
                {
                    var tc = Type.GetTypeCode(type);

                    if (tc == TypeCode.Single || tc == TypeCode.Decimal || tc == TypeCode.Double)
                        flags |= TypeFlags.Float;
                    else if (tc == TypeCode.Byte || tc == TypeCode.Int16 || tc == TypeCode.Int32 || tc == TypeCode.Int64 ||
                        tc == TypeCode.UInt16 || tc == TypeCode.UInt32 || tc == TypeCode.UInt64 || tc == TypeCode.SByte)
                        flags |= TypeFlags.Integer;

                }
                else
                    flags |= TypeFlags.Enum;
            }

            if ((flags & TypeFlags.ValueTypeFlags) == TypeFlags.None)
            {
            }

            return flags;
        }

        public static bool IsNumericType(this Type type)
        {
            if (type == null)
                throw new ArgumentNullException("type");

            return IsNumericType(GetTypeFlags(type));
        }

        public static bool IsNumericType(this TypeFlags flags)
        {
            return (flags & (TypeFlags.Float | TypeFlags.Integer)) != TypeFlags.None;
        }

        public static CollectionInfo GetCollectionInfo(this Type type)
        {
            CollectionInfo info;

            if (CollectionInfo.TryCreate(type, out info))
                return info;
            else
                return null;
        }
    }
}
